package net.cmkj.mine.util;

import com.jfinal.kit.StrKit;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.mvel2.CompileException;
import org.mvel2.MVEL;
import org.mvel2.PropertyAccessException;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Map;
import java.util.stream.Collectors;

public class CalculateUtil {
    
    private static Logger log = LogManager.getLogger(CalculateUtil.class);

    /**
     * 根据传入的公式和参数进行计算
     *
     * @param formula
     * @param variables
     * @return
     */
    public static <T> BigDecimal calculate(String formula, Map<String, T> variables) {
        if (StrKit.isBlank(formula)) {
            log.info("MVEL formula can't be null! formula : " + formula);//公式不能为空
            return new BigDecimal(0);
        }
        if (variables == null || variables.size() == 0) {
            log.info("MVEL variables can't be null! variables : " + String.valueOf(variables)); // 参数不能为空
            return new BigDecimal(0);
        }
        try {
            variables.entrySet().stream().filter(e -> e != null && e.getKey() != null && e.getValue() != null)
                    .map(CalculateUtil::convert).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        } catch (NumberFormatException e) {
            log.info("MVEL can't convert to BigDecimal, please check the variables : " + String.valueOf(variables) + "!", e);
            return new BigDecimal(0);
        }catch (Exception e) {
            e.printStackTrace();
            log.info(e.getMessage());
            return new BigDecimal(0);
        }
        BigDecimal result = null;
        try {
            result = (BigDecimal) MVEL.eval(formula, variables);
        } catch (PropertyAccessException pae){
            log.info("MVEL please check the formula :" + formula + " & variables : " + String.valueOf(variables) + "!", pae);
            return new BigDecimal(0);
        } catch (CompileException ce){
            log.info("MVEL calculate error! ", ce);
            return new BigDecimal(0);
        } catch (Exception e) {
            e.printStackTrace();
            log.info(e.getMessage());
            return new BigDecimal(0);
        }
        return result;
    }

    /**
     * 将参数转化为Bigdecimal类型
     *
     * @param entry
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Map.Entry<String, T> convert(Map.Entry<String, T> entry) {
        if (entry != null) {
            BigDecimal value = null;
            T v = entry.getValue();
            if (v instanceof BigDecimal) {
                value = (BigDecimal) entry.getValue();
            } else if(v instanceof String) {
                value = new BigDecimal((String) v);
            } else if (v instanceof BigInteger) {
                value = new BigDecimal((BigInteger) v);
            } else if (v instanceof Number) {
                value = new BigDecimal(((Number) v).doubleValue());
            } else {
                throw new ClassCastException("Not possible to coerce [" + v + "] from class " + v.getClass() + " into a BigDecimal.");
            }
            entry.setValue((T) value);
        }
        return entry;
    }

}
